/// <summary>
/// Version: GPL3
/// 
/// ArmoryLib2 
/// Copyright (C) 2008  Carl Quirion
///
/// This program is free software: you can redistribute it and/or modify
/// it under the terms of the GNU General Public License as published by
/// the Free Software Foundation, either version 3 of the License, or
/// (at your option) any later version.
/// 
/// This program is distributed in the hope that it will be useful,
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
/// GNU General Public License for more details.
/// 
/// You should have received a copy of the GNU General Public License
/// along with this program.  If not, see <http://www.gnu.org/licenses/>.
/// </summary>
using System;
using System.Collections.Generic;
using System.Xml;
using System.Web;
using System.Net;

using C = ArmoryLib2.Character.Character;
using G = ArmoryLib2.Guild.Guild;

using Res = ArmoryLib2.Character.ResistancesDetail;
using M = ArmoryLib2.Character.MeleeDetail;
using R = ArmoryLib2.Character.RangedDetail;
using D = ArmoryLib2.Character.DefenseDetail;
using S = ArmoryLib2.Character.SpellDetail;

using ArmoryLib2.Character;
using ArmoryLib2.Exceptions;
using ArmoryLib2.Guild;


namespace ArmoryLib2
{
    public class Armory
    {
        private ICache _cache;
        private ICache Cache
        {
            get { return _cache; }
            set { _cache = value; }
        }

        private Region _region;
        public Region Region
        {
            get { return _region; }
            set { _region = value; }
        }

        private string _userAgent;
        public string UserAgent
        {
            get { return _userAgent; }
            set { _userAgent = value; }
        }

        public string Url
        {
            get
            {
                switch (Region)
                {
                    case Region.USA:
                    case Region.Oceanic:
                        return "http://www.wowarmory.com/";
                    case Region.Europe:
                        return "http://eu.wowarmory.com/";
                    case Region.Korea:
                        return "http://kr.wowarmory.com/";
                    case Region.China:
                        return "http://cn.wowarmory.com/";
                    case Region.Taiwan:
                        return "http://tw.wowarmory.com/";
                }

                throw new InvalidRegionException();
            }
        }

        public string DefaultUserAgent
        {
            get
            {
                return "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.5.20404)";
            }
        }

        #region Constructors
        // Defaults to European Armory and NoCaching
        public Armory() : this(Region.Europe, new NoCache()) { }

        public Armory(ICache cache) : this(Region.Europe, cache) { }

        public Armory(Region region) : this(region, new NoCache()) { }

        public Armory(Region region, ICache cache)
        {
            Region = region;
            Cache = cache;
            UserAgent = DefaultUserAgent;
        }
        #endregion

        internal XmlDocument Request(string command)
        {
            XmlDocument cachedItem = Cache.GetItem(command);

            if (cachedItem == null)
            {
                string armoryRequest = Url + command;
                XmlDocument armoryResponse = new XmlDocument();

                using (WebClient client = new WebClient())
                {
                    client.Headers.Set("User-Agent", UserAgent);
                    armoryResponse.LoadXml(client.DownloadString(armoryRequest));
                }

                Cache.StoreItem(command, armoryResponse);
                cachedItem = armoryResponse;
            }

            return cachedItem;
        }

        #region Former Extensions (Not supported in C# 2.0)
        //TODO: QUICK HACK TO GET THINGS GOING, REWRITE CLEVERLY....
        #region GuildExtensions

        public List<G> SearchGuild(string guildName)
        {
            // TODO: Take multiple pages into account!

            // http://eu.wowarmory.com/search.xml?searchQuery=The%20Dominion&searchType=guilds
            string searchString = string.Format("search.xml?searchQuery={0}&searchType=guilds",
                                                HttpUtility.UrlEncode(guildName));

            XmlDocument searchResults = Request(searchString);
            XmlNodeList guildNodes = searchResults.SelectNodes("/page/armorySearch/searchResults/guilds/guild");

            List<G> guilds = new List<G>();

            foreach (XmlNode guildNode in guildNodes)
            {
                // <guild battleGroup="Vindication" faction="Horde" factionId="1" name="The Dominion" realm="Sporeggar" url="r=Sporeggar&amp;n=The+Dominion&amp;p=1"/>
                G guild = new G(
                    GuildDetail.Basic,
                    Region,
                    (Faction)Enum.Parse(typeof(Faction), guildNode.Attributes["factionId"].Value),
                    guildNode.Attributes["name"].Value,
                    guildNode.Attributes["realm"].Value,
                    guildNode.Attributes["battleGroup"].Value,
                    guildNode.Attributes["url"].Value);

                guilds.Add(guild);
            }

            return guilds;
        }

        // TODO: add overload to specify level of detail to load
        public G LoadGuild(string realmName, string guildName)
        {
            // http://eu.wowarmory.com/guild-info.xml?r=Sporeggar&n=The+Dominion&p=1
            // http://eu.wowarmory.com/guildheader.xml?r=Sporeggar&n=The+Dominion (battlegroup, members, faction)

            string searchString = string.Format("guild-info.xml?r={0}&n={1}&p=1",
                // TODO: Multi-page results, probably p=2, p=3, ...
                                                HttpUtility.UrlEncode(realmName),
                                                HttpUtility.UrlEncode(guildName));

            XmlDocument searchResults = Request(searchString);
            XmlNode guildDetails = searchResults.SelectSingleNode("/page/guildKey");

            if (guildDetails != null)
            {
                string searchDetailsString = string.Format("guildheader.xml?r={0}&n={1}",
                                                           HttpUtility.UrlEncode(realmName),
                                                           HttpUtility.UrlEncode(guildName));

                XmlDocument searchDetailsResults = Request(searchDetailsString);
                XmlNode guildDetailsMemberCount = searchDetailsResults.SelectSingleNode("/page/guildHeader/members");
                XmlNode guildDetailsBattleGroup = searchDetailsResults.SelectSingleNode("/page/guildHeader/battleGroup");

                // <guildKey factionId="1" name="The Dominion" nameUrl="The+Dominion" realm="Sporeggar" realmUrl="Sporeggar" url="r=Sporeggar&amp;n=The+Dominion"/>
                G guild = new G(
                    GuildDetail.Basic | GuildDetail.Roster,
                    Region,
                    (Faction)Enum.Parse(typeof(Faction), guildDetails.Attributes["factionId"].Value),
                    guildDetails.Attributes["name"].Value,
                    guildDetails.Attributes["realm"].Value,
                    guildDetailsBattleGroup.Attributes["value"].Value,
                    guildDetails.Attributes["url"].Value,
                    guildDetails.Attributes["nameUrl"].Value,
                    guildDetails.Attributes["realmUrl"].Value,
                    Convert.ToInt32(guildDetailsMemberCount.Attributes["value"].Value));

                XmlNodeList guildMembers = searchResults.SelectNodes("/page/guildInfo/guild/members/character");

                foreach (XmlNode guildMember in guildMembers)
                {
                    // <character class="Rogue" classId="4" gender="Female" genderId="1" level="70" name="Zoing" race="Blood Elf" raceId="10" rank="0" url="r=Sporeggar&amp;n=Zoing"/>
                    guild.Members.Add(new C(
                                          this,
                                          CharacterDetail.Basic,
                                          Region,
                                          guild.Faction,
                                          guildMember.Attributes["name"].Value,
                                          guild.Realm,
                                          guild.BattleGroup,
                                          (Gender)Enum.Parse(typeof(Gender), guildMember.Attributes["genderId"].Value),
                                          (Race)Enum.Parse(typeof(Race), guildMember.Attributes["raceId"].Value),
                                          (Class)Enum.Parse(typeof(Class), guildMember.Attributes["classId"].Value),
                                          Convert.ToInt32(guildMember.Attributes["level"].Value),
                                          guildMember.Attributes["url"].Value,
                                          guild));
                }

                return guild;
            }
            else
            {
                return null;
            }
        }

        #endregion

        #region CharacterExtensions
        public List<C> SearchCharacter(string characterName)
        {
            // TODO: Take multiple pages into account!

            // http://eu.wowarmory.com/search.xml?searchQuery=Zoing&searchType=characters
            string searchString = string.Format("search.xml?searchQuery={0}&searchType=characters",
                                                HttpUtility.UrlEncode(characterName));

            XmlDocument searchResults = Request(searchString);
            XmlNodeList characterNodes = searchResults.SelectNodes("/page/armorySearch/searchResults/characters/character");

            List<C> characters = new List<C>();

            foreach (XmlNode characterNode in characterNodes)
            {
                // <character battleGroup="Vindication" battleGroupId="61" class="Rogue" classId="4" faction="Horde" factionId="1" gender="Female" genderId="1" guild="The Dominion" guildId="222341" level="70" name="Zoing" race="Blood Elf" raceId="10" realm="Sporeggar" url="r=Sporeggar&amp;n=Zoing"/>
                G guild = new G(
                    GuildDetail.Basic,
                    Region,
                    (Faction)Enum.Parse(typeof(Faction), characterNode.Attributes["factionId"].Value),
                    characterNode.Attributes["guild"].Value,
                    characterNode.Attributes["realm"].Value,
                    characterNode.Attributes["battleGroup"].Value,
                    string.Format("r={0}&amp;n={1}&amp;p=1",
                                  characterNode.Attributes["realm"].Value,
                                  characterNode.Attributes["guild"].Value));

                C character = new C(
                    this,
                    CharacterDetail.Basic,
                    Region,
                    (Faction)Enum.Parse(typeof(Faction), characterNode.Attributes["factionId"].Value),
                    characterNode.Attributes["name"].Value,
                    characterNode.Attributes["realm"].Value,
                    characterNode.Attributes["battleGroup"].Value,
                    (Gender)Enum.Parse(typeof(Gender), characterNode.Attributes["genderId"].Value),
                    (Race)Enum.Parse(typeof(Race), characterNode.Attributes["raceId"].Value),
                    (Class)Enum.Parse(typeof(Class), characterNode.Attributes["classId"].Value),
                    Convert.ToInt32(characterNode.Attributes["level"].Value),
                    characterNode.Attributes["url"].Value,
                    guild);

                characters.Add(character);
            }

            return characters;
        }


        public C LoadCharacter(string realmName, string characterName)
        {
            return LoadCharacter(realmName, characterName, CharacterDetail.Basic);
        }
 
        public C LoadCharacter(string realmName, string characterName, CharacterDetail loadDetail)
        {
            XmlNode characterDetails = Util.LoadDetailNode(this, CharacterDetail.Basic, realmName, characterName);

            if (characterDetails != null)
            {
                XmlNode characterNode = characterDetails.SelectSingleNode("character");


                //TODO: Find a better way to get out
                if (characterNode == null) return null;
                // <character battleGroup="Vindication" charUrl="r=Sporeggar&amp;n=Zoing" class="Rogue" classId="4" faction="Horde" factionId="1" gender="Female" genderId="1" guildName="The Dominion" guildUrl="r=Sporeggar&amp;n=The+Dominion&amp;p=1" lastModified="31 August 2008" level="70" name="Zoing" prefix="" race="Blood Elf" raceId="10" realm="Sporeggar" suffix="">
                G guild = new G(
                            GuildDetail.Basic,
                            Region,
                            (Faction)Enum.Parse(typeof(Faction), characterNode.Attributes["factionId"].Value),
                            characterNode.Attributes["guildName"].Value ?? "",
                            characterNode.Attributes["realm"].Value,
                            characterNode.Attributes["battleGroup"].Value,
                            characterNode.Attributes["guildUrl"] == null ? "" : characterNode.Attributes["guildUrl"].Value);

                C character = new C(
                                                    this,
                                                    CharacterDetail.Basic,
                                                    Region,
                                                    (Faction)Enum.Parse(typeof(Faction), characterNode.Attributes["factionId"].Value),
                                                    characterNode.Attributes["name"].Value,
                                                    characterNode.Attributes["realm"].Value,
                                                    characterNode.Attributes["battleGroup"].Value,
                                                    (Gender)Enum.Parse(typeof(Gender), characterNode.Attributes["genderId"].Value),
                                                    (Race)Enum.Parse(typeof(Race), characterNode.Attributes["raceId"].Value),
                                                    (Class)Enum.Parse(typeof(Class), characterNode.Attributes["classId"].Value),
                                                    Convert.ToInt32(characterNode.Attributes["level"].Value),
                                                    characterNode.Attributes["charUrl"].Value,
                                                    guild);


                //if (loadDetail.ContainsDetail(CharacterDetail.CharacterSheet))
                if (CharacterDetailExtensions.ContainsDetail(loadDetail, CharacterDetail.CharacterSheet))
                {
                    character.LoadDetail(CharacterDetail.CharacterSheet);
                }

                if (CharacterDetailExtensions.ContainsDetail(loadDetail, CharacterDetail.Reputation))
                {
                    character.LoadDetail(CharacterDetail.Reputation);
                }

                if (CharacterDetailExtensions.ContainsDetail(loadDetail, CharacterDetail.Skills))
                {
                    character.LoadDetail(CharacterDetail.Skills);
                }

                if (CharacterDetailExtensions.ContainsDetail(loadDetail, CharacterDetail.Talents))
                {
                    character.LoadDetail(CharacterDetail.Talents);
                }

                return character;
            }
            else
            {
                return null;
            }
        }




    }
        #endregion
        #endregion
}


